package com.xgk.boot.module.core.logistics.service;


import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.xgk.boot.framework.common.constant.ErrorCodeConstants;
import com.xgk.boot.framework.common.context.ElmOrderThreadContext;
import com.xgk.boot.framework.common.exception.ServiceException;
import com.xgk.boot.framework.common.exception.util.ServiceExceptionUtil;
import com.xgk.boot.framework.common.util.json.JsonUtils;
import com.xgk.boot.module.core.common.RedisHandlerService;
import com.xgk.boot.framework.redis.core.RedisLockManager;
import com.xgk.boot.framework.security.core.util.SecurityFrameworkUtils;
import com.xgk.boot.module.core.controller.admin.price.vo.PriceRuleVO;
import com.xgk.boot.module.core.controller.admin.product.vo.product.ProductInfoVO;
import com.xgk.boot.module.core.dal.dto.TruckOrderDto;
import com.xgk.boot.module.core.dal.entity.product.ProductDO;
import com.xgk.boot.module.core.dal.entity.truck.TruckOrdersDO;
import com.xgk.boot.module.core.dal.redis.RedisKeyConstants;
import com.xgk.boot.module.core.dal.redis.RedisLockConstants;
import com.xgk.boot.module.core.logistics.service.mothership.dto.OrderQuotationDto;
import com.xgk.boot.module.core.logistics.service.mothership.dto.QuotationChoiceDto;
import com.xgk.boot.module.core.logistics.service.mothership.response.InvalidFieldsRequiredDto;
import com.xgk.boot.module.core.logistics.service.mothership.response.OrderDto;
import com.xgk.boot.module.core.logistics.service.mothership.response.RateDto;
import com.xgk.boot.module.core.service.product.ProductServiceImpl;
import com.xgk.boot.module.core.service.truck.TruckOrdersServiceImpl;
import com.xgk.boot.module.core.util.CommonUtil;
import com.xgk.boot.module.core.util.price.PriceCalcUtil;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 卡车物流核心流程处理
 * 包含：订单创建、相关校验、订单状态变更等
 * 同步记录每步流程日志等
 * <p>
 * 订单的基本流程：
 * 1、创建订单：填写、保存、获取报价、选择报价下单，根据返回价格跟新订单状态和账单
 * 2、定期获取订单状态：获取订单状态，更新订单状态（或者通过订单webHook的方式跟新订单状态），如果订单状态异常，可能需要发送通过email
 * 3、订单取消审批：如果订单状态是待接单或者待发货，可以取消订单，取消订单后需要更新订单状态和账单等
 * 4、订单完成：订单完成后需要更新订单状态和账单等
 * 5、订单异常处理：如果订单状态异常，需要进行处理，比如重新获取订单状态，或者发送邮件通知等
 * 6、自动重试：如果订单状态获取失败或者订单状态更新失败，可以进行自动重试，重试次数和间隔时间可以配置
 * 7、订单日志记录：每一步流程都需要记录日志，方便后续查询和分析
 */
@Slf4j
@Service
public class TruckOrderCommonService {

    @Resource
    private TruckShipperChannelService shipperChannelService;
    @Resource
    private ProductServiceImpl productService;

    @Resource
    private TruckOrdersServiceImpl truckOrdersService;

    @Resource
    private OrderHandlerService orderHandlerService;
    @Resource
    private RedisLockManager lockManager;



    /**
     * 获取服务商报价列表
     * @param orderInfo
     * @return
     */
    public List<RateDto> getLogisticsQuotationList(TruckOrderDto orderInfo) {
        TruckOrdersDO ordersDO = orderInfo.getTruckOrdersDO();
        if(StrUtil.isBlank(ordersDO.getOrderCode())){
            ordersDO.setOrderCode(CommonUtil.getNewOrderCode());
        }
        preOrderHandle(ordersDO);

        //todo：轮训对应所有服务商，汇总报价后返回
        List<ProductDO> userProductList = new ArrayList<>();
        if(StrUtil.isBlank(ordersDO.getProductCode())){
            userProductList = findCurrentUserProductList();
        }else {
            ProductDO productByCode = productService.getProductCacheByCode(ordersDO.getProductCode());
            if(productByCode == null) {
                throw ServiceExceptionUtil.exception(ErrorCodeConstants.REQUEST_EXCEPTION,"商品不存在");
            }
            userProductList.add(productByCode);
        }
        if(userProductList.isEmpty()){
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.REQUEST_EXCEPTION,"用户未配置产品信息");
        }

        List<RateDto> rateDtoList = new ArrayList<>();
        for (ProductDO productDO : userProductList) {
            //todo: 后期考虑规划多线程并发
            orderInfo.getTruckOrdersDO().setProductCode(productDO.getPrCode());
//            orderInfo.setShipperCode(productDO.getPrSpApiPd());
            log.info("向服务商获取报价,{}",orderInfo.getTruckOrdersDO().getOrderCode());
            try {
                List<RateDto> logisticsQuotationList = shipperChannelService.getLogisticsQuotationList(orderInfo);
                rateDtoList.addAll( afterQuotationOrderHandle(orderInfo,logisticsQuotationList,productDO));
            }catch (ServiceException | IOException e) {
                if(e instanceof ServiceException) {
                    ServiceException error =(ServiceException) e;
                    if(error.getCode().equals(ErrorCodeConstants.PRODUCT_CHANNEL_EXISTS.getCode())) {
                        log.error("产品服务商API：{}未实现", productDO.getPrSpApiPd());
                        continue;
                    }
                    if (error.getCode().equals(ErrorCodeConstants.COMMON_NOT_SUPPORTED.getCode()) ){
                        log.error("服务商api：{}不支持得接口", productDO.getPrSpApiPd());
                        continue;
                    }
                }
                log.error("获取报价失败，{}",e);
     //            truckOrdersService.updateOrderStatus(order.getOrderCode(), "获取报价失败");
                throw ServiceExceptionUtil.exception(ErrorCodeConstants.REQUEST_EXCEPTION, e.getMessage());
            }
        }
        afterOrderHandle();
        if(!rateDtoList.isEmpty()){
            //排序，价格由低到高
            rateDtoList = rateDtoList.stream()
                    .sorted(Comparator.comparing(RateDto::getPrice))
                    .collect(Collectors.toList());
        }
        return rateDtoList;
    }



    private void preOrderHandle(TruckOrdersDO order) {
        ElmOrderThreadContext.putOrderCode(order.getOrderCode());
        ElmOrderThreadContext.putRefrenceNo(order.getReferenceNo());
    }

    private void afterOrderHandle() {
        ElmOrderThreadContext.removeOrderCode();
        ElmOrderThreadContext.removefrenceNo();
    }

    /**
     * 根据用户查找对应商品列表
     */
    public List<ProductDO> findCurrentUserProductList() {
        String loginUserName = SecurityFrameworkUtils.getLoginUserName();
        if(StrUtil.isBlank(loginUserName)){
           throw ServiceExceptionUtil.exception(ErrorCodeConstants.REQUEST_EXCEPTION,"用户未登录");
        }
        return productService.listProductCodeByCustomer(loginUserName);
    }


    /**
     * 下单完成后计费
     *  card_rate 信用卡费率,booking_fee 手续费,test_segment分段附加费
     *  计算公式： basePrice + basePrice*附加费率+ 手续费 + （basePrice + basePrice*附件费率+ 手续费 ）*信用卡费率
     * 物流询价预算后根据价格规则计算新的报价信息，返回给客户端
     * @param orderInfo
     */
    private List<RateDto> afterQuotationOrderHandle(TruckOrderDto orderInfo,List<RateDto> rateDtos,ProductDO productDO) {
        String productCode = orderInfo.getTruckOrdersDO().getProductCode();
        ProductInfoVO productInfo = productService.getProductInfoByCode(productCode);
        List<PriceRuleVO> baseTradingFees = productInfo.getBaseTradingFees();
//        String calculationRule = baseTradingFee.getCalculationRule();
//        BigDecimal ruleValue = baseTradingFee.getRuleValue();
        Map<String, PriceRuleVO> feeMap = baseTradingFees.stream()
                .collect(Collectors.toMap(
                        PriceRuleVO::getRuleCode,  // 作为 Map 的 key
                        rule -> rule       // 作为 Map 的 value
                ));
        PriceRuleVO surcharge = null;
        for (PriceRuleVO baseTradingFee : baseTradingFees) {
            if(!baseTradingFee.getRuleCode().equals("card_rates") && !baseTradingFee.getRuleCode().equals("booking_fee")){
                surcharge = baseTradingFee;
            }
        }

        for (RateDto rateDto : rateDtos) {
            BigDecimal price = rateDto.getPrice();
//            BigDecimal newPrice = price;
//            PriceRuleVO lastRule = null;
//            for (PriceRuleVO baseTradingFee : baseTradingFees) {
//                if(baseTradingFee.getRuleCode().toLowerCase().contains("card_rate")){
//                    lastRule = baseTradingFee;
//                    continue;
//                }
//                newPrice = newPrice.add(PriceCalcUtil.calculatePriceAddChargeByRule(baseTradingFee, price));
//            }
//            if(lastRule !=null){
//                newPrice = newPrice.add(PriceCalcUtil.calculatePriceAddChargeByRule(lastRule, newPrice));
//            }
//            BigDecimal newPrice = PriceCalcUtil.calculatePriceByRule(baseTradingFee, price);
//              amanda计算方式
//            BigDecimal surchargePrice = BigDecimal.ZERO;
//            if(surcharge !=null){
//                surchargePrice= PriceCalcUtil.calculatePriceAddChargeByRule(surcharge, price);
//            }
//            BigDecimal serviceCharge = BigDecimal.ZERO;
//            if(feeMap.get("booking_fee")!=null){
//                serviceCharge= PriceCalcUtil.calculatePriceAddChargeByRule(feeMap.get("booking_fee"), price);
//            }
//            BigDecimal basePrice = price.add(surchargePrice).add(serviceCharge);
//
//            BigDecimal cardCharge = BigDecimal.ZERO;
//            if(feeMap.get("card_rates")!=null){
//                cardCharge= PriceCalcUtil.calculatePriceAddChargeByRule(feeMap.get("card_rates"), basePrice);
//            }
//            BigDecimal lastPrice =basePrice.add(cardCharge);



            BigDecimal serviceCharge = BigDecimal.ZERO;
            if(feeMap.get("booking_fee")!=null){
                serviceCharge= PriceCalcUtil.calculatePriceAddChargeByRule(feeMap.get("booking_fee"), price);
            }
            BigDecimal basePrice = serviceCharge.add(price);

            BigDecimal cardCharge = BigDecimal.ZERO;
            if(feeMap.get("card_rates")!=null){
                cardCharge= PriceCalcUtil.calculatePriceAddChargeByRule(feeMap.get("card_rates"), basePrice);
            }
            BigDecimal afterRatePrice =basePrice.add(cardCharge);

            BigDecimal surchargePrice = BigDecimal.ZERO;
            if(surcharge !=null){
                surchargePrice= PriceCalcUtil.calculatePriceAddChargeByRule(surcharge, afterRatePrice);
            }
            BigDecimal lastPrice =afterRatePrice.add(surchargePrice);

            rateDto.setPrice(lastPrice);

            rateDto.setProductCode(productCode);
            rateDto.setSpCode(productDO.getPrSpApiPd());

            if(rateDto.getProvider().equalsIgnoreCase("mothership")){
                //todo： 名称隐藏
                rateDto.setProvider("CGK");
            }
        }
        return rateDtos;
    }



    /**
     * 下单,订单同步服务商
     */
    public Boolean createOrder(TruckOrderDto orderInfo,OrderQuotationDto orderQuotationDto) {
//        preOrderCheck(orderQuotationDto);
        preOrderHandle(orderInfo.getTruckOrdersDO());
        //获取锁，然后才能操作
        String lockName = RedisLockConstants.ORDER_CREATE + orderInfo.getTruckOrdersDO().getOrderCode();

        boolean locked = false;
        //下单前置费用检查预扣（冻结）费用预扣（或者锁定部分费用，真实创建订单返回后核销等）
        orderHandlerService.preCreatOrderFeeCheck(orderInfo.getTruckOrdersDO(), orderQuotationDto);
        String createOrderMsg ="create order success";
        try {
            locked = lockManager.tryLock(lockName,10, 50);
            if(!locked){
                log.error("获取订单创建锁失败,{}", lockName);
                throw ServiceExceptionUtil.exception(ErrorCodeConstants.REQUEST_EXCEPTION, "get order sync lock failed,"+lockName);
            }
            log.info("向服务商同步订单,{}",orderInfo.getTruckOrdersDO().getOrderCode());
            orderInfo.setTruckOrderQuotationChoice(orderQuotationDto);
            OrderDto logisticsOrder = shipperChannelService.createLogisticsOrder(orderInfo);
            log.info("向服务商同步订单成功,{}", JSONUtil.toJsonStr(logisticsOrder));
            orderHandlerService.afterCreatOrderSuccessHandler(orderInfo,logisticsOrder);
            return true;
        } catch (Exception e) {
            createOrderMsg ="create order failed";
            orderHandlerService.afterCreatOrderFailedHandler(orderInfo, "获取报价失败");
            log.error("向服务商同步订单失败,订单号：{}, 错误信息：{}", orderInfo.getTruckOrdersDO().getOrderCode(), e);
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.REQUEST_EXCEPTION, e.getMessage());
        }finally {
            if (locked) {
                lockManager.unlock(lockName);
            }
            orderHandlerService.rebackPreCreatOrderFeeCheck(orderInfo.getTruckOrdersDO(), orderQuotationDto, createOrderMsg);
            afterOrderHandle();
        }
    }


//    private BigDecimal calculationOrderPrice(TruckOrdersDO truckOrdersDO, BigDecimal srcPrice){
//        String productCode = truckOrdersDO.getProductCode();
//        ProductInfoVO productInfo = productService.getProductInfoByCode(productCode);
//        PriceRuleVO baseTradingFee = productInfo.getBaseTradingFee();
////        String calculationRule = baseTradingFee.getCalculationRule();
////        BigDecimal ruleValue = baseTradingFee.getRuleValue();
//        return PriceCalcUtil.calculatePriceByRule(baseTradingFee, srcPrice);
//    }

    /**
     * 获取服务商订单详情（和服务商更新订单）
     */
    public Boolean getOrderDetail(TruckOrderDto orderInfo) {

        preOrderHandle(orderInfo.getTruckOrdersDO());
        TruckOrdersDO truckOrdersDO = orderInfo.getTruckOrdersDO();
        log.info("向服务商获取订单详情,{}",truckOrdersDO.getOrderCode());
        if(truckOrdersDO.getTrackingNumber() == null) {
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.COMMON_INFO_EXISTS, "tracking_number");
        }
        String lockName = RedisLockConstants.ORDER_UPDATE+truckOrdersDO.getOrderCode();
        boolean locked =false;
        try {
            //TODO：获取锁，然后才能操作
            locked = lockManager.tryLock(lockName,10, 50);
            if(!locked){
                log.error("向服务商获取订单锁失败,{}", lockName);
                throw ServiceExceptionUtil.exception(ErrorCodeConstants.REQUEST_EXCEPTION, "get order lock failed,"+lockName);
            }
            OrderDto logisticsOrderDetail = shipperChannelService.getLogisticsOrderDetail(orderInfo);
            orderHandlerService.afterGetOrderSuccessHandler(orderInfo,logisticsOrderDetail);

            return true;
        } catch (Exception e) {
            log.error("获取服务商订单详情失败", e);
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.REQUEST_EXCEPTION, e.getMessage());
        }finally {
            if(locked) {
                lockManager.unlock(lockName);
            }
            afterOrderHandle();
        }
    }

    /**
     * 向服务商获取订单物流信息
     * @param orderInfo
     * @return
     */
    public Boolean getOrderTracking(TruckOrderDto orderInfo){
        preOrderHandle(orderInfo.getTruckOrdersDO());
        TruckOrdersDO truckOrdersDO = orderInfo.getTruckOrdersDO();
        log.info("向服务商获取订单详情,{}",truckOrdersDO.getOrderCode());
        if(truckOrdersDO.getTrackingNumber() == null) {
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.COMMON_INFO_EXISTS, "tracking_number");
        }
        String lockName = RedisLockConstants.ORDER_UPDATE+truckOrdersDO.getOrderCode();
        boolean locked =false;
        try {
            //TODO：获取锁，然后才能操作
            locked = lockManager.tryLock(lockName,10, 50);
            if(!locked){
                log.error("向服务商获取订单物流锁失败,{}", lockName);
                throw ServiceExceptionUtil.exception(ErrorCodeConstants.REQUEST_EXCEPTION, "get order lock failed,"+lockName);
            }
            OrderDto orderTracking = shipperChannelService.getLogisticsOrderTracking(orderInfo);
            orderHandlerService.afterGetOrderTrackingSuccessHandler(orderInfo,orderTracking.getOrderTracks());

            return true;
        } catch (Exception e) {
            log.error("获取服务商订单物流详情失败", e);
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.REQUEST_EXCEPTION, e.getMessage());
        }finally {
            if(locked) {
                lockManager.unlock(lockName);
            }
            afterOrderHandle();
        }
    }


    /**
     * 向服务商获取跟新提单信息
     * @param orderInfo
     * @return
     */
    public Boolean getOrUpdateOrderBol(TruckOrderDto orderInfo){
        preOrderHandle(orderInfo.getTruckOrdersDO());
        TruckOrdersDO truckOrdersDO = orderInfo.getTruckOrdersDO();
        log.info("向服务商获取订单详情,{}",truckOrdersDO.getOrderCode());
        if(truckOrdersDO.getTrackingNumber() == null) {
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.COMMON_INFO_EXISTS, "tracking_number");
        }
        String lockName = RedisLockConstants.ORDER_UPDATE+truckOrdersDO.getOrderCode();
        boolean locked =false;
        try {
            //TODO：获取锁，然后才能操作
            locked = lockManager.tryLock(lockName,10, 50);
            if(!locked){
                log.error("向服务商获取订单物流锁失败,{}", lockName);
                throw ServiceExceptionUtil.exception(ErrorCodeConstants.REQUEST_EXCEPTION, "get order lock failed,"+lockName);
            }
            OrderDto orderTracking = shipperChannelService.getLogisticsOrderBols(orderInfo);
            //获取到提单信息后置处理
            orderHandlerService.afterGetOrderBolsSuccessHandler(orderInfo,orderTracking.getOrderBols());

            return true;
        } catch (Exception e) {
            log.error("获取服务商订单物流详情失败", e);
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.REQUEST_EXCEPTION, e.getMessage());
        }finally {
            if(locked) {
                lockManager.unlock(lockName);
            }
            afterOrderHandle();
        }
    }
}
